Try using a Targetted Gene Correlation Network to identify the LFY1 and LFY2 networks

Install…

BiocManager::install(c("AnnotationDBi", "GO.db", "preprocessCore", "impute", "rrvgo", "ComplexHeatmap"))
BiocManager::install(c("sva", "GOSim"))
install.packages("MLmetrics")
remotes::install_local("~/Downloads/GOSim_1.40.0.tgz") # get download link from https://bioconductor.org/packages/3.18/bioc/html/GOSim.html
remotes::install_github('juanbot/CoExpNets') 
# remotes::install_github("aliciagp/TGCN") # don't use this, use mine, see below!

use my modified version of TGCN

# remotes::install_local("~/git/TGCN/", force = TRUE)
# or from web:
remotes::install_github("https://github.com/jnmaloof/TGCN", ref = "dev")
library(TGCN)
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.4     ✔ tidyr     1.3.1
✔ purrr     1.0.4     ── Conflicts ────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(ggpubr)
library(gridExtra)

Attaching package: ‘gridExtra’

The following object is masked from ‘package:dplyr’:

    combine
library(gplots)

Attaching package: ‘gplots’

The following object is masked from ‘package:stats’:

    lowess
library(patchwork)
sample.description <- read.csv("../output/sample.description.sporophyte.all.csv")

load read counts. These should be log2 cpm or Voom transformed or something similar.

lcpm <- read.csv("../output/sporophyte_combined_log2cpm.csv.gz", row.names = 1, check.names = FALSE) 
rownames(lcpm) <- str_remove(rownames(lcpm), fixed(".v2"))
head(lcpm)
dim(lcpm)
[1] 29409   109

Get the GO info and convert to a list.

GOs <- read_delim("../../McConnell_Samples/input/Crichardii_676_v2.1.annotation_info.txt")
Rows: 75253 Columns: 16── Column specification ────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (16): #pacId, locusName, transcriptName, peptideName, Pfam, Panther, ec, KOG, KO, GO...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
GOs <- GOs[match(rownames(lcpm), GOs$transcriptName),] %>% drop_na(transcriptName)
gene2GO.list <- GOs %>% 
  select(transcriptName, GO) %>%
  rowwise() %>%
  mutate(GOlist = str_split(GO, pattern = " ")) %>%
  ungroup() %>%
  mutate(GOlist = set_names(GOlist, transcriptName)) %>%
  pull(GOlist)

Looking at the example notebook on the github site, it looks like genes in rows, samples in columns

My genes of interest.

CrLFY1 <- "Ceric.33G031700"

CrLFY2 <- "Ceric.18G076300"

Subset data to build LFY1 and LFY2 networks. Want to find networks around expression of LFY1 or LFY2, so pull those out and also create matrices that don’t have them.

LFY1.expression <- lcpm[str_detect(rownames(lcpm), CrLFY1),] %>%
  unlist() %>%
  as.vector()

input_for_LFY1 <- lcpm[str_detect(rownames(lcpm), CrLFY1, negate = TRUE),]

LFY2.expression <- lcpm[str_detect(rownames(lcpm), CrLFY2),] %>%
  unlist() %>%
  as.vector()

input_for_LFY2 <- lcpm[str_detect(rownames(lcpm), CrLFY2, negate = TRUE),]

LFY1 network

if(dir.exists("../output/TGCN_LFY1"))   system("rm -r ../output/TGCN_LFY1")

if(!dir.exists("../output/TGCN_LFY1"))  dir.create("../output/TGCN_LFY1")

r.lfy1 <- testAllCutoffs(exprData=input_for_LFY1,
                    target=LFY1.expression,
                    covs=NULL,
                    train.split=0.7,
                    nfolds=5,
                    t=10,
                    path="../output/TGCN_LFY1",
                    targetName="LFY1",
                    tissueName="ALL",
                    seed=3333,
                    cutoffs=10:1,
                    n=100, 
                    m=10, 
                    s=10, 
                    minCor=0.3,
                    maxTol=3,
                    save=T,
                    overwrite=T,
                    approach="enrichment", # the approach selected to complete the seed modules
                    report=F,              # if report=T, an automated report will be created
                    gene2GO=gene2GO.list,
                    cellTypeAnnotation=FALSE)
- Step 1: Create a linear regression model for each gene set of hubs based on their ratio of appearance 
Apply LASSO 10 times for feature selection 
Warning: No enrichment can pe performed - there are no feasible GO terms!Warning: No enrichment can pe performed - there are no feasible GO terms!
Number of hubs per ratio of appearance
cutoff10  cutoff9  cutoff8  cutoff7  cutoff6  cutoff5  cutoff4  cutoff3  cutoff2  cutoff1 
       0        1        5       10       15       22       32       60      108      318 
Cutoffs selected are 8 7 6 5 4 3 2 1 
Get final model and the cv error for cutoff 8 
Get final model and the cv error for cutoff 7 
Get final model and the cv error for cutoff 6 
Get final model and the cv error for cutoff 5 
Get final model and the cv error for cutoff 4 
Get final model and the cv error for cutoff 3 
Get final model and the cv error for cutoff 2 
Get final model and the cv error for cutoff 1 

- Step 2: represent train and test error per ratio of appearance model

- Step 3: creating a network for each ratio of appearance where the number of hubs is <=30

 Step 3.1: TGCN creation for ratio of appearance 8 
Warning: No enrichment can pe performed - there are no feasible GO terms!Warning: No enrichment can pe performed - there are no feasible GO terms!

 Step 3.2: TGCN characterization for cutoff 8 
Warning: No enrichment can pe performed - there are no feasible GO terms!

 Step 3.1: TGCN creation for ratio of appearance 7 

 Step 3.2: TGCN characterization for cutoff 7 

 Step 3.1: TGCN creation for ratio of appearance 6 

 Step 3.2: TGCN characterization for cutoff 6 

 Step 3.1: TGCN creation for ratio of appearance 5 

 Step 3.2: TGCN characterization for cutoff 5 
r.lfy1$selectRatio$nHubs + r.lfy1$selectRatio$stats

Focus on 7

p <- lapply(r.lfy1$nets, function(cutoff) cutoff$GOenrich$plotStats) 

          p$c7 + theme(text=element_text(size=10))

r.lfy1$nets$c7$net$moduleSizeSelectionPlot

Correlation with trait, I assume

r.lfy1$nets$c7$net$plotCorr

r.lfy1$nets$c7$net$plotCorr

DT::datatable(r.lfy1$nets$c7$net$modules)
knitr::include_graphics("../output/TGCN_LFY1/results/LFY1_ALL_c7_TGCN_crossTabPlot.png")

grid.arrange(r.lfy1$nets$c7$GOenrich$plotStats, r.lfy1$nets$c7$GOenrich$plotNterms, nrow=2)

LFY2 network

if(dir.exists("../output/TGCN_LFY2"))   system("rm -r ../output/TGCN_LFY2")

if(!dir.exists("../output/TGCN_LFY2"))  dir.create("../output/TGCN_LFY2")

r.lfy2 <- testAllCutoffs(exprData=input_for_LFY2,
                    target=LFY2.expression,
                    covs=NULL,
                    train.split=0.7,
                    nfolds=5,
                    t=10,
                    path="../output/TGCN_LFY2",
                    targetName="LFY2",
                    tissueName="ALL",
                    seed=3333,
                    cutoffs=10:1,
                    n=100, 
                    m=10, 
                    s=10, 
                    minCor=0.3,
                    maxTol=3,
                    save=T,
                    overwrite=T,
                    approach="enrichment", # the approach selected to complete the seed modules
                    report=F,              # if report=T, an automated report will be created
                    gene2GO=gene2GO.list,
                    cellTypeAnnotation=FALSE)
- Step 1: Create a linear regression model for each gene set of hubs based on their ratio of appearance 
Apply LASSO 10 times for feature selection 
Number of hubs per ratio of appearance
cutoff10  cutoff9  cutoff8  cutoff7  cutoff6  cutoff5  cutoff4  cutoff3  cutoff2  cutoff1 
       0        1        2        4        6       11       24       44       99      264 
Cutoffs selected are 8 7 6 5 4 3 2 1 
Get final model and the cv error for cutoff 8 
Get final model and the cv error for cutoff 7 
Get final model and the cv error for cutoff 6 
Get final model and the cv error for cutoff 5 
Get final model and the cv error for cutoff 4 
Get final model and the cv error for cutoff 3 
Get final model and the cv error for cutoff 2 
Get final model and the cv error for cutoff 1 

- Step 2: represent train and test error per ratio of appearance model

- Step 3: creating a network for each ratio of appearance where the number of hubs is <=30

 Step 3.1: TGCN creation for ratio of appearance 8 

 Step 3.2: TGCN characterization for cutoff 8 

 Step 3.1: TGCN creation for ratio of appearance 7 

 Step 3.2: TGCN characterization for cutoff 7 

 Step 3.1: TGCN creation for ratio of appearance 6 

 Step 3.2: TGCN characterization for cutoff 6 

 Step 3.1: TGCN creation for ratio of appearance 5 

 Step 3.2: TGCN characterization for cutoff 5 

 Step 3.1: TGCN creation for ratio of appearance 4 

 Step 3.2: TGCN characterization for cutoff 4 
r.lfy2$selectRatio$nHubs + r.lfy2$selectRatio$stats

Focus on 5

p <- lapply(r.lfy2$nets, function(cutoff) cutoff$GOenrich$plotStats) 

ggarrange(p$c5 + theme(text=element_text(size=10)), 
          p$c6 + theme(text=element_text(size=10)), 
          ncol=2, nrow=1, common.legend=T, legend="bottom")

r.lfy2$nets$c5$net$moduleSizeSelectionPlot

Correlation with trait, I assume

r.lfy2$nets$c5$net$plotCorr

DT::datatable(r.lfy2$nets$c5$net$modules)
knitr::include_graphics("../output/TGCN_LFY2/results/LFY2_ALL_c5_TGCN_crossTabPlot.png")

grid.arrange(r.lfy2$nets$c5$GOenrich$plotStats, r.lfy2$nets$c5$GOenrich$plotNterms, nrow=2)

Heat Maps

LFY1

C7

LFY1.c7 <- read_csv("../output/TGCN_LFY1/results/LFY1_ALL_c7_TGCN.csv")
Rows: 160 Columns: 3── Column specification ────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (2): hubGene, genes
dbl (1): cor
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
LFY1.c7

All genes

rbind(LFY1=LFY1.expression,lcpm[unique(LFY1.c7$genes),]) %>%
  as.matrix() %>%
  heatmap.2(trace="none", cexRow= 0.6, cexCol=0.7, col="bluered", scale="row")

hubgenes

rbind(LFY1=LFY1.expression,lcpm[unique(LFY1.c7$hubGene),]) %>%
  as.matrix() %>%
  heatmap.2(trace="none", cexRow= 0.6, cexCol=0.7, col="bluered", scale="row")

LFY2 C5

LFY2.c5 <- read_csv("../output/TGCN_LFY2/results/LFY2_ALL_c5_TGCN.csv")
Rows: 230 Columns: 3── Column specification ────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (2): hubGene, genes
dbl (1): cor
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
LFY2.c5

All genes

rbind(LFY2=LFY2.expression,lcpm[unique(LFY2.c5$genes),]) %>%
  as.matrix() %>%
  heatmap.2(trace="none", cexRow= 0.6, cexCol=0.7, col="bluered", scale="row")

hubgenes

rbind(LFY2=LFY2.expression,lcpm[unique(LFY2.c5$hubGene),]) %>%
  as.matrix() %>%
  heatmap.2(trace="none", cexRow= 0.6, cexCol=0.7, col="bluered", scale="row")

LS0tCnRpdGxlOiAiMDZfVEdDTl9zcG9yb3BoeXRlIgphdXRob3I6ICJKdWxpbiBNYWxvb2YiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgpUcnkgdXNpbmcgYSBbVGFyZ2V0dGVkIEdlbmUgQ29ycmVsYXRpb24gTmV0d29ya10oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9zNDE1OTgtMDI0LTY3MzI5LTcpIHRvIGlkZW50aWZ5IHRoZSBMRlkxIGFuZCBMRlkyIG5ldHdvcmtzCgpJbnN0YWxsLi4uCmBgYHtyLCBldmFsPUZBTFNFfQpCaW9jTWFuYWdlcjo6aW5zdGFsbChjKCJBbm5vdGF0aW9uREJpIiwgIkdPLmRiIiwgInByZXByb2Nlc3NDb3JlIiwgImltcHV0ZSIsICJycnZnbyIsICJDb21wbGV4SGVhdG1hcCIpKQpCaW9jTWFuYWdlcjo6aW5zdGFsbChjKCJzdmEiLCAiR09TaW0iKSkKaW5zdGFsbC5wYWNrYWdlcygiTUxtZXRyaWNzIikKcmVtb3Rlczo6aW5zdGFsbF9sb2NhbCgifi9Eb3dubG9hZHMvR09TaW1fMS40MC4wLnRneiIpICMgZ2V0IGRvd25sb2FkIGxpbmsgZnJvbSBodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvMy4xOC9iaW9jL2h0bWwvR09TaW0uaHRtbApyZW1vdGVzOjppbnN0YWxsX2dpdGh1YignanVhbmJvdC9Db0V4cE5ldHMnKSAKIyByZW1vdGVzOjppbnN0YWxsX2dpdGh1YigiYWxpY2lhZ3AvVEdDTiIpICMgZG9uJ3QgdXNlIHRoaXMsIHVzZSBtaW5lLCBzZWUgYmVsb3chCmBgYAoKdXNlIG15IG1vZGlmaWVkIHZlcnNpb24gb2YgVEdDTgpgYGB7ciwgZXZhbD1GQUxTRX0KIyByZW1vdGVzOjppbnN0YWxsX2xvY2FsKCJ+L2dpdC9UR0NOLyIsIGZvcmNlID0gVFJVRSkKIyBvciBmcm9tIHdlYjoKcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoImh0dHBzOi8vZ2l0aHViLmNvbS9qbm1hbG9vZi9UR0NOIiwgcmVmID0gImRldiIpCmBgYAoKCmBgYHtyfQpsaWJyYXJ5KFRHQ04pCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdncHVicikKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkoZ3Bsb3RzKQpsaWJyYXJ5KHBhdGNod29yaykKYGBgCgpgYGB7cn0Kc2FtcGxlLmRlc2NyaXB0aW9uIDwtIHJlYWQuY3N2KCIuLi9vdXRwdXQvc2FtcGxlLmRlc2NyaXB0aW9uLnNwb3JvcGh5dGUuYWxsLmNzdiIpCmBgYAoKCmxvYWQgcmVhZCBjb3VudHMuIFRoZXNlIHNob3VsZCBiZSBsb2cyIGNwbSBvciBWb29tIHRyYW5zZm9ybWVkIG9yIHNvbWV0aGluZyBzaW1pbGFyLgoKYGBge3J9CmxjcG0gPC0gcmVhZC5jc3YoIi4uL291dHB1dC9zcG9yb3BoeXRlX2NvbWJpbmVkX2xvZzJjcG0uY3N2Lmd6Iiwgcm93Lm5hbWVzID0gMSwgY2hlY2submFtZXMgPSBGQUxTRSkgCnJvd25hbWVzKGxjcG0pIDwtIHN0cl9yZW1vdmUocm93bmFtZXMobGNwbSksIGZpeGVkKCIudjIiKSkKaGVhZChsY3BtKQpkaW0obGNwbSkKYGBgCkdldCB0aGUgR08gaW5mbyBhbmQgY29udmVydCB0byBhIGxpc3QuCmBgYHtyfQpHT3MgPC0gcmVhZF9kZWxpbSgiLi4vLi4vTWNDb25uZWxsX1NhbXBsZXMvaW5wdXQvQ3JpY2hhcmRpaV82NzZfdjIuMS5hbm5vdGF0aW9uX2luZm8udHh0IikKR09zIDwtIEdPc1ttYXRjaChyb3duYW1lcyhsY3BtKSwgR09zJHRyYW5zY3JpcHROYW1lKSxdICU+JSBkcm9wX25hKHRyYW5zY3JpcHROYW1lKQpnZW5lMkdPLmxpc3QgPC0gR09zICU+JSAKICBzZWxlY3QodHJhbnNjcmlwdE5hbWUsIEdPKSAlPiUKICByb3d3aXNlKCkgJT4lCiAgbXV0YXRlKEdPbGlzdCA9IHN0cl9zcGxpdChHTywgcGF0dGVybiA9ICIgIikpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoR09saXN0ID0gc2V0X25hbWVzKEdPbGlzdCwgdHJhbnNjcmlwdE5hbWUpKSAlPiUKICBwdWxsKEdPbGlzdCkKYGBgCgpMb29raW5nIGF0IHRoZSBleGFtcGxlIG5vdGVib29rIG9uIHRoZSBnaXRodWIgc2l0ZSwgaXQgbG9va3MgbGlrZSBnZW5lcyBpbiByb3dzLCBzYW1wbGVzIGluIGNvbHVtbnMKCk15IGdlbmVzIG9mIGludGVyZXN0LgpgYGB7cn0KQ3JMRlkxIDwtICJDZXJpYy4zM0cwMzE3MDAiCgpDckxGWTIgPC0gIkNlcmljLjE4RzA3NjMwMCIKYGBgCgpTdWJzZXQgZGF0YSB0byBidWlsZCBMRlkxIGFuZCBMRlkyIG5ldHdvcmtzLiAgV2FudCB0byBmaW5kIG5ldHdvcmtzIGFyb3VuZCBleHByZXNzaW9uIG9mIExGWTEgb3IgTEZZMiwgc28gcHVsbCB0aG9zZSBvdXQgYW5kIGFsc28gY3JlYXRlIG1hdHJpY2VzIHRoYXQgZG9uJ3QgaGF2ZSB0aGVtLgpgYGB7cn0KTEZZMS5leHByZXNzaW9uIDwtIGxjcG1bc3RyX2RldGVjdChyb3duYW1lcyhsY3BtKSwgQ3JMRlkxKSxdICU+JQogIHVubGlzdCgpICU+JQogIGFzLnZlY3RvcigpCgppbnB1dF9mb3JfTEZZMSA8LSBsY3BtW3N0cl9kZXRlY3Qocm93bmFtZXMobGNwbSksIENyTEZZMSwgbmVnYXRlID0gVFJVRSksXQoKTEZZMi5leHByZXNzaW9uIDwtIGxjcG1bc3RyX2RldGVjdChyb3duYW1lcyhsY3BtKSwgQ3JMRlkyKSxdICU+JQogIHVubGlzdCgpICU+JQogIGFzLnZlY3RvcigpCgppbnB1dF9mb3JfTEZZMiA8LSBsY3BtW3N0cl9kZXRlY3Qocm93bmFtZXMobGNwbSksIENyTEZZMiwgbmVnYXRlID0gVFJVRSksXQpgYGAKCiMjIExGWTEgbmV0d29yawpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KaWYoZGlyLmV4aXN0cygiLi4vb3V0cHV0L1RHQ05fTEZZMSIpKSAgIHN5c3RlbSgicm0gLXIgLi4vb3V0cHV0L1RHQ05fTEZZMSIpCgppZighZGlyLmV4aXN0cygiLi4vb3V0cHV0L1RHQ05fTEZZMSIpKSAgZGlyLmNyZWF0ZSgiLi4vb3V0cHV0L1RHQ05fTEZZMSIpCgpyLmxmeTEgPC0gdGVzdEFsbEN1dG9mZnMoZXhwckRhdGE9aW5wdXRfZm9yX0xGWTEsCiAgICAgICAgICAgICAgICAgICAgdGFyZ2V0PUxGWTEuZXhwcmVzc2lvbiwKICAgICAgICAgICAgICAgICAgICBjb3ZzPU5VTEwsCiAgICAgICAgICAgICAgICAgICAgdHJhaW4uc3BsaXQ9MC43LAogICAgICAgICAgICAgICAgICAgIG5mb2xkcz01LAogICAgICAgICAgICAgICAgICAgIHQ9MTAsCiAgICAgICAgICAgICAgICAgICAgcGF0aD0iLi4vb3V0cHV0L1RHQ05fTEZZMSIsCiAgICAgICAgICAgICAgICAgICAgdGFyZ2V0TmFtZT0iTEZZMSIsCiAgICAgICAgICAgICAgICAgICAgdGlzc3VlTmFtZT0iQUxMIiwKICAgICAgICAgICAgICAgICAgICBzZWVkPTMzMzMsCiAgICAgICAgICAgICAgICAgICAgY3V0b2Zmcz0xMDoxLAogICAgICAgICAgICAgICAgICAgIG49MTAwLCAKICAgICAgICAgICAgICAgICAgICBtPTEwLCAKICAgICAgICAgICAgICAgICAgICBzPTEwLCAKICAgICAgICAgICAgICAgICAgICBtaW5Db3I9MC4zLAogICAgICAgICAgICAgICAgICAgIG1heFRvbD0zLAogICAgICAgICAgICAgICAgICAgIHNhdmU9VCwKICAgICAgICAgICAgICAgICAgICBvdmVyd3JpdGU9VCwKICAgICAgICAgICAgICAgICAgICBhcHByb2FjaD0iZW5yaWNobWVudCIsICMgdGhlIGFwcHJvYWNoIHNlbGVjdGVkIHRvIGNvbXBsZXRlIHRoZSBzZWVkIG1vZHVsZXMKICAgICAgICAgICAgICAgICAgICByZXBvcnQ9RiwgICAgICAgICAgICAgICMgaWYgcmVwb3J0PVQsIGFuIGF1dG9tYXRlZCByZXBvcnQgd2lsbCBiZSBjcmVhdGVkCiAgICAgICAgICAgICAgICAgICAgZ2VuZTJHTz1nZW5lMkdPLmxpc3QsCiAgICAgICAgICAgICAgICAgICAgY2VsbFR5cGVBbm5vdGF0aW9uPUZBTFNFKQpgYGAKCgoKYGBge3J9CnIubGZ5MSRzZWxlY3RSYXRpbyRuSHVicyArIHIubGZ5MSRzZWxlY3RSYXRpbyRzdGF0cwpgYGAKRm9jdXMgb24gNwoKYGBge3J9CnAgPC0gbGFwcGx5KHIubGZ5MSRuZXRzLCBmdW5jdGlvbihjdXRvZmYpIGN1dG9mZiRHT2VucmljaCRwbG90U3RhdHMpIAoKICAgICAgICAgIHAkYzcgKyB0aGVtZSh0ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEwKSkKYGBgCgoKYGBge3J9CnIubGZ5MSRuZXRzJGM3JG5ldCRtb2R1bGVTaXplU2VsZWN0aW9uUGxvdApgYGAKCkNvcnJlbGF0aW9uIHdpdGggdHJhaXQsIEkgYXNzdW1lCmBgYHtyfQpyLmxmeTEkbmV0cyRjNyRuZXQkcGxvdENvcnIKYGBgCgpgYGB7cn0Kci5sZnkxJG5ldHMkYzckbmV0JHBsb3RDb3JyCmBgYAoKCmBgYHtyfQpEVDo6ZGF0YXRhYmxlKHIubGZ5MSRuZXRzJGM3JG5ldCRtb2R1bGVzKQpgYGAKCgoKYGBge3J9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIuLi9vdXRwdXQvVEdDTl9MRlkxL3Jlc3VsdHMvTEZZMV9BTExfYzdfVEdDTl9jcm9zc1RhYlBsb3QucG5nIikKYGBgCgoKYGBge3J9CmdyaWQuYXJyYW5nZShyLmxmeTEkbmV0cyRjNyRHT2VucmljaCRwbG90U3RhdHMsIHIubGZ5MSRuZXRzJGM3JEdPZW5yaWNoJHBsb3ROdGVybXMsIG5yb3c9MikKYGBgCgoKIyMgTEZZMiBuZXR3b3JrCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQppZihkaXIuZXhpc3RzKCIuLi9vdXRwdXQvVEdDTl9MRlkyIikpICAgc3lzdGVtKCJybSAtciAuLi9vdXRwdXQvVEdDTl9MRlkyIikKCmlmKCFkaXIuZXhpc3RzKCIuLi9vdXRwdXQvVEdDTl9MRlkyIikpICBkaXIuY3JlYXRlKCIuLi9vdXRwdXQvVEdDTl9MRlkyIikKCnIubGZ5MiA8LSB0ZXN0QWxsQ3V0b2ZmcyhleHByRGF0YT1pbnB1dF9mb3JfTEZZMiwKICAgICAgICAgICAgICAgICAgICB0YXJnZXQ9TEZZMi5leHByZXNzaW9uLAogICAgICAgICAgICAgICAgICAgIGNvdnM9TlVMTCwKICAgICAgICAgICAgICAgICAgICB0cmFpbi5zcGxpdD0wLjcsCiAgICAgICAgICAgICAgICAgICAgbmZvbGRzPTUsCiAgICAgICAgICAgICAgICAgICAgdD0xMCwKICAgICAgICAgICAgICAgICAgICBwYXRoPSIuLi9vdXRwdXQvVEdDTl9MRlkyIiwKICAgICAgICAgICAgICAgICAgICB0YXJnZXROYW1lPSJMRlkyIiwKICAgICAgICAgICAgICAgICAgICB0aXNzdWVOYW1lPSJBTEwiLAogICAgICAgICAgICAgICAgICAgIHNlZWQ9MzMzMywKICAgICAgICAgICAgICAgICAgICBjdXRvZmZzPTEwOjEsCiAgICAgICAgICAgICAgICAgICAgbj0xMDAsIAogICAgICAgICAgICAgICAgICAgIG09MTAsIAogICAgICAgICAgICAgICAgICAgIHM9MTAsIAogICAgICAgICAgICAgICAgICAgIG1pbkNvcj0wLjMsCiAgICAgICAgICAgICAgICAgICAgbWF4VG9sPTMsCiAgICAgICAgICAgICAgICAgICAgc2F2ZT1ULAogICAgICAgICAgICAgICAgICAgIG92ZXJ3cml0ZT1ULAogICAgICAgICAgICAgICAgICAgIGFwcHJvYWNoPSJlbnJpY2htZW50IiwgIyB0aGUgYXBwcm9hY2ggc2VsZWN0ZWQgdG8gY29tcGxldGUgdGhlIHNlZWQgbW9kdWxlcwogICAgICAgICAgICAgICAgICAgIHJlcG9ydD1GLCAgICAgICAgICAgICAgIyBpZiByZXBvcnQ9VCwgYW4gYXV0b21hdGVkIHJlcG9ydCB3aWxsIGJlIGNyZWF0ZWQKICAgICAgICAgICAgICAgICAgICBnZW5lMkdPPWdlbmUyR08ubGlzdCwKICAgICAgICAgICAgICAgICAgICBjZWxsVHlwZUFubm90YXRpb249RkFMU0UpCmBgYAoKCgpgYGB7cn0Kci5sZnkyJHNlbGVjdFJhdGlvJG5IdWJzICsgci5sZnkyJHNlbGVjdFJhdGlvJHN0YXRzCmBgYApGb2N1cyBvbiA1CgpgYGB7cn0KcCA8LSBsYXBwbHkoci5sZnkyJG5ldHMsIGZ1bmN0aW9uKGN1dG9mZikgY3V0b2ZmJEdPZW5yaWNoJHBsb3RTdGF0cykgCgpnZ2FycmFuZ2UocCRjNSArIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTApKSwgCiAgICAgICAgICBwJGM2ICsgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMCkpLCAKICAgICAgICAgIG5jb2w9MiwgbnJvdz0xLCBjb21tb24ubGVnZW5kPVQsIGxlZ2VuZD0iYm90dG9tIikKYGBgCgpgYGB7cn0Kci5sZnkyJG5ldHMkYzUkbmV0JG1vZHVsZVNpemVTZWxlY3Rpb25QbG90CmBgYAoKQ29ycmVsYXRpb24gd2l0aCB0cmFpdCwgSSBhc3N1bWUKCmBgYHtyfQpyLmxmeTIkbmV0cyRjNSRuZXQkcGxvdENvcnIKYGBgCgoKYGBge3J9CkRUOjpkYXRhdGFibGUoci5sZnkyJG5ldHMkYzUkbmV0JG1vZHVsZXMpCmBgYAoKYGBge3J9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIuLi9vdXRwdXQvVEdDTl9MRlkyL3Jlc3VsdHMvTEZZMl9BTExfYzVfVEdDTl9jcm9zc1RhYlBsb3QucG5nIikKYGBgCgpgYGB7cn0KZ3JpZC5hcnJhbmdlKHIubGZ5MiRuZXRzJGM1JEdPZW5yaWNoJHBsb3RTdGF0cywgci5sZnkyJG5ldHMkYzUkR09lbnJpY2gkcGxvdE50ZXJtcywgbnJvdz0yKQpgYGAKCgojIyBIZWF0IE1hcHMKCiMjIyBMRlkxCgojIyMjIEM3CgpgYGB7cn0KTEZZMS5jNyA8LSByZWFkX2NzdigiLi4vb3V0cHV0L1RHQ05fTEZZMS9yZXN1bHRzL0xGWTFfQUxMX2M3X1RHQ04uY3N2IikKTEZZMS5jNwpgYGAKCkFsbCBnZW5lcwpgYGB7cn0KcmJpbmQoTEZZMT1MRlkxLmV4cHJlc3Npb24sbGNwbVt1bmlxdWUoTEZZMS5jNyRnZW5lcyksXSkgJT4lCiAgYXMubWF0cml4KCkgJT4lCiAgaGVhdG1hcC4yKHRyYWNlPSJub25lIiwgY2V4Um93PSAwLjYsIGNleENvbD0wLjcsIGNvbD0iYmx1ZXJlZCIsIHNjYWxlPSJyb3ciKQpgYGAKaHViZ2VuZXMKYGBge3J9CnJiaW5kKExGWTE9TEZZMS5leHByZXNzaW9uLGxjcG1bdW5pcXVlKExGWTEuYzckaHViR2VuZSksXSkgJT4lCiAgYXMubWF0cml4KCkgJT4lCiAgaGVhdG1hcC4yKHRyYWNlPSJub25lIiwgY2V4Um93PSAwLjYsIGNleENvbD0wLjcsIGNvbD0iYmx1ZXJlZCIsIHNjYWxlPSJyb3ciKQpgYGAKCgoKCiMjIExGWTIgQzUKCmBgYHtyfQpMRlkyLmM1IDwtIHJlYWRfY3N2KCIuLi9vdXRwdXQvVEdDTl9MRlkyL3Jlc3VsdHMvTEZZMl9BTExfYzVfVEdDTi5jc3YiKQpMRlkyLmM1CmBgYAoKQWxsIGdlbmVzCmBgYHtyfQpyYmluZChMRlkyPUxGWTIuZXhwcmVzc2lvbixsY3BtW3VuaXF1ZShMRlkyLmM1JGdlbmVzKSxdKSAlPiUKICBhcy5tYXRyaXgoKSAlPiUKICBoZWF0bWFwLjIodHJhY2U9Im5vbmUiLCBjZXhSb3c9IDAuNiwgY2V4Q29sPTAuNywgY29sPSJibHVlcmVkIiwgc2NhbGU9InJvdyIpCmBgYApodWJnZW5lcwpgYGB7cn0KcmJpbmQoTEZZMj1MRlkyLmV4cHJlc3Npb24sbGNwbVt1bmlxdWUoTEZZMi5jNSRodWJHZW5lKSxdKSAlPiUKICBhcy5tYXRyaXgoKSAlPiUKICBoZWF0bWFwLjIodHJhY2U9Im5vbmUiLCBjZXhSb3c9IDAuNiwgY2V4Q29sPTAuNywgY29sPSJibHVlcmVkIiwgc2NhbGU9InJvdyIpCmBgYA==